﻿package zf.media.audio.lrc
{
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.media.SoundChannel;
	import flash.net.URLRequest;
	import flash.net.URLStream;

	/**
	 * 歌词同步后，当每次抵达歌词时触发 
	 * @eventType lor.media.audio.lrc.LrcEvent.LRC_CHANGE
	 */
	[Event(name="lrc_change", type="zf.media.audio.lrc.LrcEvent")]

	/**
	 * 歌词载入失败时触发
	 * @eventType flash.events.ErrorEvent.ERROR
	 */
	[Event(name="error", type="flash.events.ErrorEvent")]

	/**
	 * 歌词载入完成时触发
	 * @eventType lor.media.audio.lrc.LrcEvent.LRC_LOAD_COMPLETE
	 */
	[Event(name="lrc_load_complete", type="zf.media.audio.lrc.LrcEvent")]

	/**
	 * 歌词同步结束时触发
	 * @eventType flash.events.Event.COMPLETE
	 */
	[Event(name="complete", type="flash.events.Event")]

	/**
	 * LRC歌词类。用于和MP3歌曲同步 
	 * @author Mike.zhang
	 * @langversion 3.0 
	 * @playerversion Flash 9 
	 * 
	 * @see LrcUnit
	 * @see LrcEvent
	 */
	public class Lrc extends EventDispatcher
	{
		//前面有下划线的变量均为属性变量，没有的为类用变量


		private var _lrcData:Array;
		//标题
		private var _title:String;
		//作者或专辑
		private var _author:String;
		//艺术家
		private var _artist:String;
		//歌词编辑者
		private var _by:String;
		//时间补偿值，正值推后，负值提前
		private var _offset:Number;

		private var stream:URLStream;
		private var request:URLRequest;
		private var loaded:Boolean;

		private var charset:String;

		private var tmc:Shape;
		private var ti:int;
		private var tl:int;
		private var _lrcChange:Function;
		private var sc:SoundChannel;
		//歌词变化事件的时间偏移量(毫秒)，正值将提前，负值将滞后
		private var _changeOffset:int;

		/**
		 * 创建歌词类 
		 * 
		 */
		public function Lrc ():void
		{
			//config ();
		}
		private function config ():void
		{
			stream=new URLStream  ;
			request=new URLRequest  ;
			_lrcData=new Array  ;
			_title="";
			_author="";
			_artist="";
			_by="";
			_offset=0;
			loaded=false;
			_lrcChange=new Function  ;
			_changeOffset=0;
			tmc=new Shape  ;
		}
		/**
		 * 歌词数据，其每个元素为一个LrcUnit对象 
		 * @return 数组 
		 * @see LrcUnit
		 */
		public function get lrcData ():Array
		{
			return _lrcData;
		}
		/**
		 * 歌词中附带标题信息 
		 * @return 一般是曲名
		 * 
		 */
		public function get title ():String
		{
			return _title;
		}
		/**
		 * 歌词中附带的专辑或作者信息 
		 * @return 专辑或曲作者
		 * 
		 */
		public function get author ():String
		{
			return _author;
		}
		/**
		 * 歌词中附带的艺术家信息 
		 * @return 艺术家
		 * 
		 */
		public function get artist ():String
		{
			return _artist;
		}
		/**
		 * 歌词中附带的歌词制作人或团队 
		 * @return 歌词制作者
		 * 
		 */
		public function get by ():String
		{
			return _by;
		}
		/**
		 * 歌词的时间偏移值（或叫做时间补偿值） 
		 * @return 歌词时间偏移
		 * 
		 */
		public function get offset ():Number
		{
			return _offset;
		}
		/*public function set lyricChange (f:Function):void
		{
		//设置当歌曲播放到某段歌词时调用的函数，请务必定义f有且只有一个String类型的参数用来接收歌词
		_lrcChange=f;
		}*/

		/**
		 * 歌词变化事件的时间偏移量(毫秒)，正值将提前，负值将滞后，通过设置此属性你可以获得歌词整体提前或滞后 
		 * @return 歌词变化的时间偏移量
		 * 
		 */
		public function get changeOffset ():int
		{
			return _changeOffset;
		}
		public function set changeOffset (v:int):void
		{
			_changeOffset=v;
		}
		/**
		 * 停止歌词同步 
		 * 
		 */
		public function stop ():void
		{
			if (tmc)
			{
				tmc.removeEventListener (Event.ENTER_FRAME,enterFrameEvent);
				//_lrcChange ("");
				this.dispatchEvent (new LrcEvent(LrcEvent.LRC_CHANGE,""));

			}
		}
		/**
		 * 加载歌词 
		 * @param url 要加载的歌词才URL地址
		 * @param char 要加载歌词的编码格式，默认为 gb2312
		 * 
		 */
		public function load (url:String,char:String="gb2312"):void
		{
			//读取url指定地址的lrc歌词
			//char为指定的读取方式。
			if (! url)
			{
				return;
			}
			config ();
			charset=char;
			request.url=url;
			configureListeners (stream);
			try
			{
				stream.load (request);
			}
			catch (error:Error)
			{
				dispatchEvent (new ErrorEvent(ErrorEvent.ERROR));
			}
		}
		/**
		 * 获取时间 num 处的歌词 
		 * @param num 时间
		 * @return 歌词文本
		 * 
		 */
		public function getLyric (num:uint):String
		{
			//精确获取num毫秒处的歌词，一般返回将为""
			var l:int=_lrcData.length;
			if (! l)
			{
				return "";
			}
			var index:int=seek(num,0,false);
			if (index > 0)
			{
				return _lrcData[index].lyric;
			}
			return "";
		}
		/**
		 * 模糊获取时间num处附近的歌词 
		 * @param num 时间
		 * @return 歌词文本
		 * 
		 */
		public function getBlurLyric (num:uint):String
		{
			//模糊获取num毫秒处的歌词
			var l:int=_lrcData.length;
			if (! l)
			{
				return "";
			}
			var index:int=seek(num,0,true);
			if (index > 0)
			{
				return _lrcData[index].lyric;
			}
			return "";
		}
		/**
		 * 寻找v(毫秒)指定的歌词组索引 
		 * @param v 时间，毫秒为单位
		 * @param startIndex 指定查找的开始索引，默认为0
		 * @param blur 是否模糊查找，模糊查找为找到和v时间差最小的歌词组索引。默认为模糊查找
		 * @return 索引
		 * 
		 */
		private function seek (v:int,startIndex:uint=0,blur:Boolean=true):int
		{
			var i:int=startIndex;
			var x:int=_lrcData.length - 1;
			var d:int=Math.floor(x - i / 2) + i;
			var l:int=x - i + 1;
			if (v > _lrcData[x].time || v < _lrcData[i].time)
			{
				if (blur)
				{
					return v > _lrcData[x].time?x:i;
				}
				return -1;
			}
			while (i < x)
			{
				if (v == _lrcData[i].time)
				{
					return i;
				}
				if (v == _lrcData[x].time)
				{
					return x;
				}
				if (v == _lrcData[d].time)
				{
					return d;
				}
				else
				{
					if (d == i || d == x)
					{
						if (blur)
						{
							if (d < l-1)
							{
								return Math.abs(_lrcData[d].time - v) < Math.abs(_lrcData[d + 1].time - v)?d:d + 1;
							}
							return d;
						}
						return -1;
					}
					if (v > _lrcData[d].time)
					{
						i=d;
					}
					else
					{
						x=d;
					}
					d=Math.floor(x - i / 2) + i;
				}
			}
			return -1;
		}
		/**
		 * 更新LRC，使LRC歌词与 SoundChannel 同步
		 * @param s SoundChannel的声音控制
		 * 
		 */
		public function update (s:SoundChannel):void
		{
			//现在为了提高效能，将采用ENTER_FRAME更新
			//惰性更新，需要手动调用，务必在拖动MP3播放进度时更新
			if (loaded == false || s == null)
			{
				return;
			}
			if (tmc.hasEventListener(Event.ENTER_FRAME) == false)
			{
				tmc.addEventListener (Event.ENTER_FRAME,enterFrameEvent);
			}
			ti=0;
			tl=_lrcData.length;
			sc=s;
			ti=seek(sc.position);
			lrcUpdate ();
		}
		/**
		 * 添加事件 
		 * @param dispatcher
		 * 
		 */
		private function configureListeners (dispatcher:URLStream):void
		{
			dispatcher.addEventListener (Event.COMPLETE,completeHandler);
			dispatcher.addEventListener (SecurityErrorEvent.SECURITY_ERROR,loadErrorHandler);
			dispatcher.addEventListener (HTTPStatusEvent.HTTP_STATUS,loadErrorHandler);
			dispatcher.addEventListener (IOErrorEvent.IO_ERROR,loadErrorHandler);
		}

		/**
		 * 清空操作 
		 * @param event
		 * 
		 */
		private function completeHandler (event:Event):void
		{
			event.stopPropagation ();
			stream.removeEventListener (Event.COMPLETE,completeHandler);
			stream.removeEventListener (SecurityErrorEvent.SECURITY_ERROR,loadErrorHandler);
			stream.removeEventListener (HTTPStatusEvent.HTTP_STATUS,loadErrorHandler);
			stream.removeEventListener (IOErrorEvent.IO_ERROR,loadErrorHandler);
			var ss:String=stream.readMultiByte(stream.bytesAvailable,charset);
			var arr:Array=ss.split("\r");
			readLrc (arr);
			readHeadData (arr);
			creatData (arr);
			loaded=true;
			dispatchEvent (new LrcEvent(LrcEvent.LRC_LOAD_COMPLETE,null));
		}

		private function loadErrorHandler (event:Event):void
		{
			event.stopPropagation ();
			dispatchEvent (new ErrorEvent(ErrorEvent.ERROR));
		}
		private function readLrc (a:Array):void
		{
			var l:int=a.length;
			for (var i:int=0; i < l; i++)
			{
				a[i]=a[i].split("]");

				var len:int=a[i].length;
				if (len < 2)
				{
					a.splice (i,1);
					i--;
					l--;
				}
				else
				{
					if (deleteHTSpace(a[i][1]).length == 0)
					{
						a[i][1]="";
					}
					for (var j:int=0; j < a[i].length; j++)
					{
						var ta:Array=a[i][j].split("[");
						a[i][j]=ta[ta.length - 1];
					}
				}
			}
		}
		/**
		 * 删除字符串头尾空格 
		 * @param char
		 * @return 
		 * 
		 */
		private function deleteHTSpace (char:String):String
		{
			if (char == null)
			{
				return "";
			}
			var pattern:RegExp=/^\s*/;
			char=char.replace(pattern,"");
			pattern=/\s*$/;
			return char.replace(pattern,"");
		}
		/**
		 * 读取LRC中附带信息 
		 * @param a
		 * 
		 */
		private function readHeadData (a:Array):void
		{
			var l:int=a.length;
			for (var i:int=0; i < l; i++)
			{
				if (! a[i][1])
				{
					var ta:Array=a[i][0].split(":");
					switch (ta[0].toLocaleLowerCase())
					{
						case "ar" :
							_artist=ta[1];
							a.splice (i,1);
							i--;
							l--;
							break;
						case "ti" :
							_title=ta[1];
							a.splice (i,1);
							i--;
							l--;
							break;
						case "al" :
							_author=ta[1];
							a.splice (i,1);
							i--;
							l--;
							break;
						case "by" :
							_by=ta[1];
							a.splice (i,1);
							i--;
							l--;
							break;
						case "offset" :
							_offset=parseInt(ta[1]);
							a.splice (i,1);
							i--;
							l--;
							break;

					}
				}
			}
		}
		/**
		 * 创建歌词数据 
		 * @param a 
		 * 
		 */
		private function creatData (a:Array):void
		{
			for (var i:uint=0; i < a.length; i++)
			{
				for (var j:uint=0; j < a[i].length - 1; j++)
				{
					//_lrcData.push ({time:toTime(a[i][j]),lyric:a[i][a[i].length-1]});
					_lrcData.push (new LrcUnit(toTime(a[i][j]),a[i][a[i].length - 1]));
				}
			}
			_lrcData.sortOn ("time",Array.NUMERIC);

		}
		/**
		 * 获取歌词间最小时间差，以毫秒为单位 
		 * @return 最小时间差
		 * 
		 */
		private function getMinTime ():int
		{
			//获取歌词间最小的时间间隔,返回毫秒

			var l:int=_lrcData.length;
			if (! l)
			{
				return 0;
			}
			var min:int=int.MAX_VALUE;
			for (var i:int=0; i < l - 1; i++)
			{
				var t:int=_lrcData[i + 1].time - _lrcData[i].time;
				if (t < min)
				{
					min=t;
				}
			}
			return min;
		}
		/**
		 * 转化歌词中附带的时间信息为确切的以毫秒计时的数值 
		 * @param ss
		 * @return 毫秒数值
		 * 
		 */
		private function toTime (ss:String):int
		{
			var a:Array=ss.split(":");
			var ia:Array=a[1].split(".");
			if (ia.length < 2)
			{
				ia.push ("00");
			}
			var result:int=parseInt(a[0]) * 60000;
			result+= parseInt(ia[0]) * 1000;
			result+= parseInt(ia[1]) * 10;
			return result;
		}
		private function lrcUpdate ():void
		{

			if (sc.position + _changeOffset > _lrcData[ti].time)
			{
				//_lrcChange (_lrcData[ti].lyric);
				this.dispatchEvent (new LrcEvent(LrcEvent.LRC_CHANGE,_lrcData[ti].lyric));
				ti++;
			}
			if (ti > tl - 1)
			{
				//trace("删除监听");
				tmc.removeEventListener (Event.ENTER_FRAME,enterFrameEvent);
				this.dispatchEvent (new Event(Event.COMPLETE));
			}
		}
		/**
		 * @private 
		 * 
		 */
		protected function onEnterFrameEvent ():void
		{
			lrcUpdate ();
		}
		private function enterFrameEvent (e:Event):void
		{
			onEnterFrameEvent ();
		}
	}
}